# -*- encoding: utf-8 -*-

from odoo import models, fields, api, _
from odoo.exceptions import UserError, RedirectWarning
from collections import defaultdict
from odoo.addons.base_cw.public import merge_line

INVOICE_TYPE = dict(out_invoice=u'销售',
                    in_invoice=u'采购',
                    out_refund=u'销售退回',
                    in_refund=u'采购退回', )


class account_invoice(models.Model):
    _name = 'cncw.invoice.move'
    _inherit = ['cncw.invoice.move', 'account.voucher.script']
    _description = u'发票'

    script_line_ids = fields.One2many('account.voucher.script.line', 'invoice_id', u'预切凭证明细', domain=[('picking_id', '=', False)])
    is_summary_generate_voucher = fields.Boolean(u'汇开凭证', default=False)

    in_refund_script_line_ids = fields.One2many(
        'account.voucher.script.line', 'invoice_id', string=u'红字暂估凭证明细', domain=[('picking_id', '!=', False)])

    def generate_account_voucher(self):
        super(account_invoice, self).generate_account_voucher()
        for picking_id in self.in_refund_script_line_ids.mapped('picking_id'):
            picking_id._generate_account_voucher(self.in_refund_script_line_ids.filtered(lambda r: r.picking_id == picking_id and not r.voucher_id))

    def generate_cancel_voucher_script2(self):
        '''
        取消生成的凭证
        :return:
        '''
        for invoice in self:
            invoice.in_refund_script_line_ids.unlink()

    def generate_account_voucher_script_line2(self):
        self.ensure_one()
        self.in_refund_script_line_ids.unlink()
        for picking_id in self.invoice_line_ids.mapped('stock_move_id').mapped('picking_id'):
            price_sub_total = 0.0
            for x in self.invoice_line_ids.filtered(lambda r: r.stock_move_id.picking_id == picking_id):
                price_sub_total += -x.price_subtotal
            for script_line_id in picking_id.script_line_ids.filtered(lambda r: not r.invoice_id):
                if script_line_id.dc_type == 'D':
                    copy_data = {
                        'debit': price_sub_total,
                        'lc_debit': price_sub_total,
                        'invoice_id': self.id,
                        'name': '红字回冲单:' + self.name,
                        'voucher_id': False
                    }
                else:
                    copy_data = {
                        'credit': price_sub_total,
                        'lc_credit': price_sub_total,
                        'invoice_id': self.id,
                        'name': '红字回冲单:' + self.name,
                        'voucher_id': False
                    }
                script_line_copy = script_line_id.copy(copy_data)
                if script_line_copy.account_id.sub_account_type == 'has':
                    script_line_copy.sub_account_lines = [(0,0, {'category_id':self.partner_id.subaccount_category_id.id,
                                                                 'sub_account_id': self.partner_id.id})]

    @api.model
    def get_account(self, invoice_type, partner_id=None):
        """
        取应收、应付会计科目
        :return:
        """

        config = self.env['account.voucher.template'].search([('code', '=', '01')], limit=1)
        if not config or not config.payable_account_id or not config.receivable_account_id:
            raise UserError(_(u'请在财务{凭证模版}设定中 维护应收、应付会计科目!'))
        if invoice_type in ('out_invoice', 'out_refund'):
            account_id = config.receivable_account_id.id
        else:
            account_id = config.payable_account_id.id
        return account_id

    @api.model
    def get_pay_account_product_type(self, invoice_type, product_type):
        """
        取应收、应付会计科目
        :return:
        """
        config = self.env['account.product.type'].search([('product_type', '=', product_type)], limit=1)
        if not config or not config.sale_account_id or not config.purchase_account_id:
            raise UserError(_(u'请在成本类型科目设定中设置收入/支出的会计科目!'))
        if invoice_type in ('out_invoice', 'out_refund'):
            account_id = config.sale_account_id.id
        else:
            account_id = config.purchase_account_id.id
        return account_id

    @api.model
    def get_pay_account_categ(self, invoice_type, product_id, fiscal_pos=None):
        """
        取 会计科目
        :return:
        """
        accounts = product_id.product_tmpl_id.get_product_accounts(fiscal_pos)
        if not (accounts.get('income',False) and accounts.get('expense',False)):
            raise UserError(u"请检查产品或产品对应的产品分类 会科设定")
        if invoice_type in ('out_invoice', 'out_refund'):
            return accounts['income'].id
        return accounts['expense'].id

    @api.model
    def get_pay_account(self, invoice_type, product_id=None, product_type=None, fiscal_pos=None):
        if product_type:
            return self.get_pay_account_product_type(invoice_type, product_type=product_type)
        elif product_id:
            return self.get_pay_account_product_type(invoice_type, product_id.product_type)
        else:
            return None

    @api.model
    def get_tax_account(self, type):
        """
        取进项税、销项税会计科
        :return:
        """
        config = self.env['account.voucher.template'].search([('code', '=', '01')], limit=1)
        if not config or not config.sale_tax_account_id or not config.purchase_tax_account_id:
            raise UserError(_(u'请在财务设定中 维护进项税、销项税会计科目!'))
        if type in ('out_invoice', 'out_refund'):
            account_id = config.sale_tax_account_id.id
        else:
            account_id = config.purchase_tax_account_id.id
        return account_id

    # 发票申请确认
    def action_confirm(self):
        self.ensure_one()
        res = super(account_invoice, self).action_confirm()
        self.generate_account_voucher_script()
        return res

    # 发票取消确认
    def action_cancel_confirm(self):
        for invoice in self:
            if invoice.voucher_id.line_ids and invoice.voucher_id.state not in ('cancel',):
                # if invoice.script_line_ids:
                raise UserError(u'错误提示当前发票已经生成凭证，不能取消确认！')
        super(account_invoice, self).action_cancel_confirm()
        self.generate_cancel_voucher_script()
        self.generate_cancel_voucher_script2()

    # 红字发票申请确认
    def action_invalid_invoice_confirm(self):
        """
        作废发票 确认更新相关
        :return:
        """
        self.ensure_one()
        for invoice in self:
            if invoice.voucher_id.line_ids and invoice.voucher_id.state not in ('cancel',):
                # if invoice.script_line_ids:
                raise UserError(_(u'错误提示'), _(u'当前发票已经生成凭证，不能取消确认！'))
        super(account_invoice, self).action_invalid_invoice_confirm()
        self.generate_account_voucher_script()

    # 红字发票取消确认
    def action_invalid_invoice_cancel_confirm(self):
        for invoice in self:
            if invoice.voucher_id.line_ids and invoice.voucher_id.state not in ('cancel',):
                raise UserError(_(u'错误提示'), _(u'当前发票已经生成凭证，不能取消确认！'))
        super(account_invoice, self).action_invalid_invoice_cancel_confirm()
        self.generate_cancel_voucher_script()

    def generate_account_voucher_script(self):
        '''
        生成预切凭证
        :return:
        '''
        for invoice in self:
            if invoice.voucher_id and invoice.voucher_id.state not in ('draft', 'cancel'):
                raise UserError(u'错误提示当前发票生成的凭证已经确认，不能取消！')
            invoice.generate_account_voucher_script_line()
            invoice.generate_account_voucher_script_line2()

    def generate_cancel_voucher_script(self):
        '''
        取消生成的凭证
        :return:
        '''
        for invoice in self:
            if invoice.voucher_id and invoice.voucher_id.state not in ('draft', 'cancel'):
                raise UserError(u'错误提示当前发票生成的凭证已经确认，不能取消！')
            if invoice.voucher_id.line_ids:
                invoice.voucher_id.line_ids.unlink()
            invoice.script_line_ids.unlink()

    def generate_account_voucher_script_line(self):
        """
        产生预切凭证明细 切类似凭证
        采购入库 ：
            借：    原料       xxx
                   进项税额    xxx
              贷： 应付帐款    xxx


        采购退回出库 ：
            借：   应付帐款    xxx
               贷: 原料       xxx
                   进项税额    xxx

        销售出库:
            借:      应收帐款     xxx
              贷:    主营业务收入 xxx
                     销项税额    xxx

        销售退回入库:
            借:     主营业务收入 xxx
                   销项税额    xxx
              贷:  应收帐款     xxx
        :return:
        """
        self.ensure_one()
        self.script_line_ids.unlink()
        d1 = defaultdict(lambda: defaultdict(float))
        d2 = defaultdict(lambda: defaultdict(float))
        items = []
        for x in self.invoice_line_ids:
            key = (x.account1_id, x.sub_account_lines)
            d1[key]['price_subtotal'] += x.price_subtotal
            d1[key]['price_subtotal_signed'] += x.price_subtotal_signed
            # d1[key]['sub_account_lines'] += [(0,0,sub_account_id.id) for sub_account_id in x.sub_account_lines ]
            d2[x.account2_id]['total_amount'] += x.total_amount
            d2[x.account2_id]['total_amount_signed'] += x.total_amount_signed
            # d2[x.account2_id]['sub_account_lines'] += x.sub_account_lines
        # 1.应收 应付
        move_name = '发票：%s' % self.invoice_no
        name = u'销售收入'
        if self.move_type in ('in_refund', 'in_invoice'):
            name = u'原料采购'

        dc_type = 'D'
        if self.move_type in ('in_refund', 'in_invoice'):
            dc_type = 'C'

        d_items = []
        for k, v in d2.items():
            d_vals = dict(dc_type=dc_type,
                          account_id=k.id,
                          currency_id=self.currency_id.id,
                          exchange_rate=self.exchange_rate,
                          name='%s;%s' % (name, move_name),
                          )
            if k.sub_account_type == 'has':
                line_data = {'category_id': k.subaccount_category_id.id, 'sub_account_id': self.partner_id.id or False}
                sub_account_lines_data=[(0, 0, line_data)]
                d_vals.update(dict(
                    sub_account_id=self.partner_id.id,
                    sub_account_lines=sub_account_lines_data
                ))
            if dc_type == 'D':
                # d_vals.update(dict(debit=abs(v['total_amount']), lc_debit=abs(v['total_amount_signed']), ))
                d_vals.update(dict(debit=v['total_amount'], lc_debit=v['total_amount_signed'], ))
            else:
                d_vals.update(dict(credit=v['total_amount'], lc_credit=v['total_amount_signed']))
                # d_vals.update(dict(credit=abs(v['total_amount']), lc_credit=abs(v['total_amount_signed'])))

            d_items.append((0, 0, d_vals))
        c_items = []
        # 2.销售收入 、采购原料
        dc_type = 'C'
        if self.move_type in ('in_refund', 'in_invoice'):
            dc_type = 'D'
        for k, v in d1.items():
            c_vals = dict(dc_type=dc_type,
                          account_id=k[0].id,
                          currency_id=self.currency_id.id,
                          exchange_rate=self.exchange_rate,
                          name=move_name
                          )
            if k[0].sub_account_type == 'has':
                line_data = {'category_id': self.partner_id.subaccount_category_id.id, 'sub_account_id': self.partner_id.id or False}
                sub_account_lines_data=[(0, 0, line_data)]
                c_vals.update(dict(
                    sub_account_id=self.partner_id.id or False,
                    sub_account_lines=sub_account_lines_data
                ))

            if dc_type == 'C':
                c_vals.update(dict(credit=v['price_subtotal'], lc_credit=v['price_subtotal_signed'], ))
            # c_vals.update(dict(credit=abs(v['price_subtotal']), lc_credit=abs(v['price_subtotal_signed']), ))
            else:
                # c_vals.update(dict(debit=abs(v['price_subtotal']), lc_debit=abs(v['price_subtotal_signed']), ))
                c_vals.update(dict(debit=v['price_subtotal'], lc_debit=v['price_subtotal_signed'], ))
            c_items.append((0, 0, c_vals))
        # 3.销项税 、进项税
        for x in self.invoice_line_ids2:
            c_vals = dict(dc_type=dc_type,
                          account_id=x.account_id.id,
                          currency_id=self.currency_id.id,
                          exchange_rate=self.exchange_rate,
                          name=move_name,  # 摘要,
                          )
            if dc_type == 'C':
                # c_vals.update(dict(credit=abs(x.amount), lc_credit=abs(x.tax_amount_signed), ))
                c_vals.update(dict(credit=x.amount, lc_credit=x.tax_amount_signed, ))
            else:
                # c_vals.update(dict(debit=abs(x.amount), lc_debit=abs(x.tax_amount_signed), ))
                c_vals.update(dict(debit=x.amount, lc_debit=x.tax_amount_signed, ))
            c_items.append((0, 0, c_vals))
        items += d_items + c_items
        # items = merge_line(self, items)
        self.write(dict(script_line_ids=items))
        super(account_invoice, self).generate_account_voucher_script_line()

    def invoice_check(self):
        super(account_invoice, self).invoice_check()
        if self.voucher_id and self.voucher_id.state != 'cancel':
            raise  UserError(_(u'提示发票已生成记帐凭证，取消凭证之前不可以取消发票.'))

    def _assert_balanced(self):
        if not self.ids:
            return True
        prec = self.env['decimal.precision'].precision_get('Amount')
        # 正则小数几位
        f_rule = '%.{}f'.format(max(5, prec))
        # sql = """
        #         SELECT  master_id
        #           FROM  account_voucher_script_line
        #          WHERE  master_id in %s
        #       GROUP BY  master_id
        #         HAVING  abs(sum(debit) - sum(credit)) > %s
        #     """ % (tuple(self.ids), 10 ** (-max(5, prec)))
        self._cr.execute("""
                SELECT invoice_id
                  FROM account_voucher_script_line
                 WHERE invoice_id in %s and picking_id is null
              GROUP BY invoice_id
                HAVING abs(sum(debit) - sum(credit)) > %s
            """, (tuple(self.ids),  float(f_rule % 10 ** (-max(5, prec)))))  # 默认取六位小数  10 ** (-max(5, prec))
        if len(self._cr.fetchall()) != 0:
            raise UserError(_(u"预切凭证借贷不平，请检查."))
        self._cr.execute("""
                        SELECT invoice_id
                          FROM account_voucher_script_line
                         WHERE invoice_id in %s and picking_id is not null
                      GROUP BY invoice_id
                        HAVING abs(sum(debit) - sum(credit)) > %s
                    """, (tuple(self.ids), float(f_rule % 10 ** (-max(5, prec)))))  # 默认取六位小数  10 ** (-max(5, prec))
        if len(self._cr.fetchall()) != 0:
            raise UserError(_(u"红字暂估凭证借贷不平，请检查."))
        return True


class account_move_line(models.Model):
    _inherit = 'cncw.invoice.move.line'

    def create(self,vals):
        record = super(account_move_line, self).create(vals)
        if record.account_id and record.account2_id.sub_account_type == 'has':
            val = {'category_id': record.move_id.partner_id.subaccount_category_id.id,
                   'sub_account_id': record.move_id.partner_id.id}
            record.sub_account_lines = [(0, 0, val)]
        return record